package security

import (
	"fmt"
	"gitee.com/kristas/booting-go/framework/common/util/lang"
	"gitee.com/kristas/booting-go/framework/core/log"
	"gitee.com/kristas/booting-go/framework/core/statement/types"
	"gitee.com/kristas/booting-go/framework/data/datasource"
	"gitee.com/kristas/booting-go/plugin/gorm_plugin"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	"github.com/casbin/gorm-adapter/v3"
	"gorm.io/gorm"
)

/*
https://casbin.org/docs/zh-CN/overview
*/

func NewStarter() *Casbin {
	return &Casbin{}
}

type Casbin struct {
	types.Component
	e     *casbin.Enforcer
	db    *gorm.DB
	Model map[string]string `value:"casbin.model"`
	Log   log.Logger        `wire:""`
}

func (r *Casbin) DataSource() string {
	return "casbin.datasource"
}

func (r *Casbin) SetDB(db datasource.DB) {
	r.db = gorm_plugin.MustBindDB(db)
}

func (r *Casbin) Init() error {
	e, err := InitDatabase(r.Model, r.db)
	if err != nil {
		panic(err)
	}
	r.e = e
	return nil
}

func (r Casbin) GetEnforcer() *casbin.Enforcer {
	return r.e
}

func InitDatabase(m map[string]string, db *gorm.DB) (*casbin.Enforcer, error) {
	adapter, err := gormadapter.NewAdapterByDB(db)
	if err != nil {
		return nil, err
	}
	newModelFromString, err := model.NewModelFromString(parseModel(m))
	if err != nil {
		return nil, err
	}
	e, err := casbin.NewEnforcer(newModelFromString, adapter)
	if err != nil {
		return nil, err
	}
	_, err = e.AddPolicy("Administrator", "/*", "(GET)|(POST)|(PUT)|(PATCH)|(OPTIONS)")
	if err != nil {
		return nil, err
	}
	_, err = e.AddGroupingPolicy("admin", "Administrator")
	if err != nil {
		return nil, err
	}
	return e, nil
}

func parseModel(m map[string]string) string {
	s := lang.NewEmptyString()
	for k, v := range m {
		s = s.Concat(fmt.Sprintf("[%s]\n%s\n", k, v))
	}
	return s.String()
}
